home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / Libraries / SpriteEngine / SpriteTools.c < prev    next >
Text File  |  1995-03-13  |  9KB  |  339 lines

  1. // SpriteTools
  2. // Routines to be called from the engine and from SpriteHandlers
  3.  
  4.  
  5. #include "SpriteTools.h"
  6.  
  7. /*
  8. MyNewGWorld: Creates a GWorld
  9. LoadFaceFromCicn: Loads a face
  10. PlotFace: Draws a face
  11.  
  12. NewSprite: Creates a sprite
  13. DisposeSprite: Disposes a sprite
  14.  
  15. KeepOnScreen: Performs border checks for a sprite
  16. RectSeparate: Moves two sprites apart
  17.  
  18. */
  19.  
  20.  
  21. /*Global variables*/
  22.  
  23. /* The window pointer */
  24. WindowPtr myWindow;
  25.  
  26. /* A global pointer is the root of the entity list */
  27. SpritePtr    gSpriteList = nil;
  28.  
  29. /* GWorlds for the animation and background buffers */
  30. GrafPtr gOffScreen, gBackScreen;
  31.  
  32.  
  33.  
  34. /*MyNewGWorld: Glue to NewGWorld*/
  35. /*I declare offscreenGWorld as GrafPtr to save us a bunch of typecasts later (in CopyBits).*/
  36. /*Most parameters to NewGWorld omitted - NewGWorld is smart enough to make the defaults useable.*/
  37.  
  38. #include <QDOffScreen.h>
  39.  
  40. void MyNewGWorld(GrafPtr *offscreenGWorld, Rect *boundsRect)
  41. {
  42.     GDHandle saveGD;
  43.     GWorldPtr savePort;
  44.  
  45.     GetGWorld(&savePort, &saveGD);
  46.  
  47.     if ( noErr != NewGWorld((GWorldPtr *)offscreenGWorld, 0, boundsRect, nil, nil, pixelsLocked) )
  48.         DoError;
  49. /*We lock the offscreen pixmap so we can CopyBits and PlotCIcon to it.*/
  50.     if ( LockPixels((*(CGrafPtr *)offscreenGWorld)->portPixMap) )
  51.         ;
  52. /*Note: We should unlock it (UnlockPixels) when not animating, to avoid memory fragmentation,*/
  53. /*but you can bother with that later if it's a problem.*/
  54.     SetGWorld(savePort, saveGD);
  55. }; /*MyNewGWorld*/
  56.  
  57.  
  58.  
  59. GrafPtr LoadFaceFromCicn(short cicnId)
  60. {
  61.     GrafPtr offscreenGWorld;
  62.     CIconHandle theCicn;
  63.     GDHandle saveGD;
  64.     GWorldPtr savePort;
  65.  
  66.     GetGWorld(&savePort, &saveGD);
  67.     theCicn = GetCIcon(cicnId);
  68.     MyNewGWorld(&offscreenGWorld, &(**theCicn).iconMask.bounds);
  69.     if ( offscreenGWorld != nil )
  70.     {
  71.         SetGWorld((GWorldPtr)offscreenGWorld, nil);
  72.         PlotCIcon(&(**theCicn).iconMask.bounds, theCicn);
  73.  
  74. /*I use the clipRgn for storing the mask region. This may seem dangerous,
  75. but when we aren't drawing in the GWorld anyway, it won't matter.*/
  76.         if ( offscreenGWorld == nil )
  77.             offscreenGWorld->clipRgn = NewRgn ();
  78.         if ( noErr != BitMapToRegion(offscreenGWorld->clipRgn, &(**theCicn).iconMask) )/**/
  79.             offscreenGWorld->clipRgn = nil;/*or DisposeRgn?*/
  80.  
  81.         DisposeCIcon(theCicn);
  82.     }
  83.     SetGWorld(savePort, saveGD);
  84.     return offscreenGWorld;
  85. } /*LoadFaceFromCicn*/
  86.  
  87.  
  88. static RgnHandle gTmpRgn = nil;
  89.  
  90. void PlotFace(GrafPtr theCicn, GrafPtr destPort, Point where)
  91. {
  92.     GDHandle saveGD;
  93.     GWorldPtr savePort;
  94.     Rect bounds;
  95.     RGBColor saveForeColor, saveBackColor;
  96.  
  97.     GetGWorld(&savePort, &saveGD);
  98.     bounds = theCicn->portRect;
  99.     OffsetRect(&bounds, where.h - bounds.left, where.v - bounds.top);
  100.  
  101.     if ( gTmpRgn == nil )
  102.         gTmpRgn = NewRgn (); /*For top speed, we make this global, and create it only once!*/
  103.     CopyRgn(theCicn->clipRgn, gTmpRgn);
  104.     OffsetRgn(gTmpRgn, where.h, where.v);
  105.     SetPort(destPort);  /*I assume that the device is correctly set.*/
  106.     GetForeColor(&saveForeColor);
  107.     GetBackColor(&saveBackColor);
  108.     ForeColor(blackColor);
  109.     BackColor(whiteColor);
  110.     CopyBits(&theCicn->portBits, &destPort->portBits, &theCicn->portRect, &bounds, srcCopy, gTmpRgn);
  111.     RGBForeColor(&saveForeColor);
  112.     RGBBackColor(&saveBackColor);
  113.     SetGWorld(savePort, saveGD);
  114. } /*PlotFace*/
  115.  
  116.  
  117.  
  118.  
  119. /*************************************/
  120. /* Routines for sprite list handling */
  121. /*************************************/
  122.  
  123.  
  124. /* NewSprite allocates space for a new entity and puts it in the entity list */
  125.  
  126. SpritePtr NewSprite()
  127. {
  128.     SpritePtr    who;
  129.  
  130.     who = (SpritePtr) NewPtr(sizeof(SpriteRecord));
  131.     if (who == nil) return nil;
  132.     if (gSpriteList != nil)
  133.     {
  134.         gSpriteList->prev = who;
  135.     }
  136.     who->next = gSpriteList;
  137.     who->prev = nil;
  138.     gSpriteList = who;
  139.     return who;
  140. } /*NewSprite*/
  141.  
  142.  
  143. /* DisposeSprite removes an entity from the list and disposes it. */
  144.  
  145. void DisposeSprite(SpritePtr who)
  146. {
  147.     if (who == nil) return;
  148.     if (who->next != nil)
  149.         who->next->prev = who->prev;
  150.     if (who->prev != nil)
  151.         who->prev->next = who->next;
  152.     if (who == gSpriteList)
  153.         gSpriteList = who->next;
  154.     DisposePtr((Ptr)who);
  155. } /*DisposeSprite*/
  156.  
  157.  
  158. /*** End of sprite handling routines ***/
  159.  
  160.  
  161. /* KeepOnScreen makes border checks to keep the sprite within the window.
  162. on a border hit, the speed is negated in order to make the sprite bounce.
  163. KeepOnScreen returns true if a border was hit. */
  164.  
  165. Boolean KeepOnScreen(SpritePtr theSprite)
  166. {
  167.     Boolean    returnValue = false;
  168.     
  169.     if (theSprite->position.h < 0)
  170.     {
  171.         theSprite->position.h = 0;
  172.         theSprite->speed.h = abs(theSprite->speed.h);
  173.         returnValue = true;
  174.     }
  175.     if (theSprite->position.v < 0)
  176.     {
  177.         theSprite->position.v = 0;
  178.         theSprite->speed.v = abs(theSprite->speed.v);
  179.         returnValue = true;
  180.     }
  181.     if (theSprite->position.h > gOffScreen->portRect.right - theSprite->face->portRect.right)
  182.     {
  183.         theSprite->position.h = gOffScreen->portRect.right - theSprite->face->portRect.right;
  184.         theSprite->speed.h = -abs(theSprite->speed.h);
  185.         returnValue = true;
  186.     }
  187.     if (theSprite->position.v > gOffScreen->portRect.bottom - theSprite->face->portRect.bottom)
  188.     {
  189.         theSprite->position.v = gOffScreen->portRect.bottom - theSprite->face->portRect.bottom;
  190.         theSprite->speed.v = -abs(theSprite->speed.v);
  191.         returnValue = true;
  192.     }
  193.     
  194.     return returnValue;
  195. } /*KeepOnScreen*/
  196.  
  197.  
  198. #ifdef _hasfixedpoint
  199. /*Same as above, but also modifies the fixedPointPosition field*/
  200. Boolean KeepOnScreenFixed(SpritePtr theSprite)
  201. {
  202.     Boolean    returnValue = false;
  203.  
  204.     if (theSprite->position.h < 0)
  205.     {
  206.         theSprite->position.h = 0;
  207.         theSprite->fixedPointPosition.h = 0;
  208.         theSprite->speed.h = abs(theSprite->speed.h);
  209.         returnValue = true;
  210.     }
  211.     if (theSprite->position.v < 0)
  212.     {
  213.         theSprite->position.v = 0;
  214.         theSprite->fixedPointPosition.v = 0;
  215.         theSprite->speed.v = abs(theSprite->speed.v);
  216.         returnValue = true;
  217.     }
  218.     if (theSprite->position.h > gOffScreen->portRect.right - theSprite->face->portRect.right)
  219.     {
  220.         theSprite->position.h = gOffScreen->portRect.right - theSprite->face->portRect.right;
  221.         theSprite->fixedPointPosition.h = theSprite->position.h << 4;
  222.         theSprite->speed.h = -abs(theSprite->speed.h);
  223.         returnValue = true;
  224.     }
  225.     if (theSprite->position.v > gOffScreen->portRect.bottom - theSprite->face->portRect.bottom)
  226.     {
  227.         theSprite->position.v = gOffScreen->portRect.bottom - theSprite->face->portRect.bottom;
  228.         theSprite->fixedPointPosition.v = theSprite->position.v << 4;
  229.         theSprite->speed.v = -abs(theSprite->speed.v);
  230.         returnValue = true;
  231.     }
  232.     
  233.     return returnValue;
  234. } /*KeepOnScreenFixed*/
  235. #endif
  236.  
  237.  
  238. /* Moves two sprites apart, to separate them with respect to their bounding boxes. */
  239.  
  240. short RectSeparate(SpritePtr theSprite, SpritePtr anotherSprite)
  241. {
  242.     short    distance[4], shortest, shortestDistance, i;
  243.     Rect    bounds1, bounds2;
  244.  
  245.     bounds1 = theSprite->face->portRect;
  246.     OffsetRect(&bounds1, theSprite->position.h, theSprite->position.v);
  247.  
  248.     bounds2 = anotherSprite->face->portRect;
  249.     OffsetRect(&bounds2, anotherSprite->position.h, anotherSprite->position.v);
  250.  
  251. /*Calculate the distance to separate the sprites in every direction*/
  252.     distance[0] = bounds2.top - bounds1.bottom; //up
  253.     distance[1] = bounds2.bottom - bounds1.top; //down
  254.     distance[2] = bounds2.right - bounds1.left; //right
  255.     distance[3] = bounds2.left - bounds1.right; //left
  256.  
  257. /*Find the shortest distance*/
  258.     shortest = 0;
  259.     shortestDistance = abs(distance[0]);
  260.     for (i=1; i<4; i++)
  261.     {
  262.         if (abs(distance[i]) < shortestDistance)
  263.         {
  264.             shortest = i;
  265.             shortestDistance = abs(distance[i]);
  266.         }
  267.     }
  268.  
  269. /*Move the sprite in the appropriate direction*/
  270.     switch (shortest)
  271.     {
  272.         case 0:
  273.         case 1:
  274.             theSprite->position.v += distance[shortest]; break;
  275.         case 2:
  276.         case 3:
  277.             theSprite->position.h += distance[shortest]; break;
  278.     }
  279.     return shortest;
  280. } /*RectSeparate*/
  281.  
  282.  
  283. /* Random number from 0 to range-1 */
  284.  
  285. short Rand(short range)
  286. {
  287.     short roll;
  288.     
  289.     roll = Random();
  290.     return (abs(roll) % range);
  291. } /*Rand*/
  292.  
  293.  
  294.  
  295. /* Collision test using regions! */
  296.  
  297. Boolean    RegionHit(SpritePtr theSprite, SpritePtr anotherSprite)
  298. {
  299.     RgnHandle    faceRegion1, faceRegion2;
  300.     Boolean    result;
  301.  
  302.     faceRegion1 = NewRgn();
  303.     faceRegion2 = NewRgn();
  304.  
  305.     CopyRgn(theSprite->face->clipRgn, faceRegion1);
  306.     OffsetRgn(faceRegion1, theSprite->position.h, theSprite->position.v);
  307.  
  308.     CopyRgn(anotherSprite->face->clipRgn, faceRegion2);
  309.     OffsetRgn(faceRegion2, anotherSprite->position.h, anotherSprite->position.v);
  310.     
  311.     SectRgn(faceRegion1, faceRegion2, faceRegion1);
  312.     result = !EmptyRgn(faceRegion1);
  313.     
  314.     DisposeRgn(faceRegion1);
  315.     DisposeRgn(faceRegion2);
  316.     
  317.     return    result;
  318. } /*RegionHit*/
  319.  
  320.  
  321. /* Split a vector v into a component p parallel to another vector d,
  322. and a compionent n that is perpendicular to d. Useful for realistic
  323. collision handling! */
  324.  
  325. void SplitVector(Point v, Point d, Point *p, Point *n)
  326. {
  327.     long length2, dotProduct;
  328.  
  329.     length2 = d.h * d.h + d.v * d.v;    /*Squared length of "d"*/
  330.  
  331.     dotProduct = v.h * d.h + v.v * d.v;    /*Scalar product*/
  332.  
  333.     (*p).h = d.h * dotProduct / length2;
  334.     (*p).v = d.v * dotProduct / length2;
  335.     (*n).h = v.h - (*p).h;
  336.     (*n).v = v.v - (*p).v;
  337. } /* SplitVector */
  338.  
  339.